﻿using System;
using System.Collections.Generic;
using System.Globalization;

using Argos.Collections;

namespace Argos.Scheduling
{
	/// <summary>
	/// A list of weekdays.
	/// </summary>
	/// <remarks>
	/// This list does not allow duplicate occurrences of weekdays.
	/// </remarks>
	[Serializable]
	public sealed class WeekdayList : UniqueList<DayOfWeek>
	{

		public WeekdayList() { }

		public WeekdayList(IEnumerable<DayOfWeek> collection) : base(collection) { }



		#region public static WeekdayList AllDays { get; }

		/// <summary>
		/// Returns a list containing all weekdays.
		/// </summary>
		public static WeekdayList AllDays
		{
			get
			{
				WeekdayList list = new WeekdayList();
				list.AddAllDays();
				return list;
			}
		}

		#endregion

		#region public static WeekdayList Today { get; }

		/// <summary>
		/// Returns a list with the current weekday.
		/// </summary>
		public static WeekdayList Today
		{
			get
			{
				WeekdayList list = new WeekdayList();
				list.AddToday();
				return list;
			}
		}

		#endregion

		#region public static WeekdayList Weekend {get; }

		/// <summary>
		/// Returns a list containing Saturday and Sunday.
		/// </summary>
		public static WeekdayList Weekend
		{
			get
			{
				WeekdayList list = new WeekdayList();
				list.AddWeekend();
				return list;
			}
		}

		#endregion

		#region public static WeekdayList WorkDays { get; }

		/// <summary>
		/// Returns a list containing Monday thru Friday.
		/// </summary>
		public static WeekdayList WorkDays
		{
			get
			{
				WeekdayList list = new WeekdayList();
				list.AddWorkDays();
				return list;
			}
		}

		#endregion



		private DayOfWeek[] weekdayArray = new DayOfWeek[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday };



		#region public void AddAllDays()

		/// <summary>
		/// Adds all weekdays to the list.
		/// </summary>
		public void AddAllDays()
		{
			List<DayOfWeek> list = new List<DayOfWeek>();
			list.AddRange(this.weekdayArray);
			list.AddRange(this.weekdayArray);

			DayOfWeek day = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
			this.Add(day);

			int i = list.IndexOf(day);
			while (this.Count < 7)
			{
				i++;
				this.Add(list[i]);
			}
		}

		#endregion

		#region public void AddToday()

		/// <summary>
		/// Adds the current day to the list.
		/// </summary>
		public void AddToday()
		{
			this.Add(DateTime.Now.DayOfWeek);
		}

		#endregion

		#region public void AddWorkDays()

		/// <summary>
		/// Adds Monday thru Friday to the list.
		/// </summary>
		public void AddWorkDays()
		{
			this.Add(DayOfWeek.Monday);
			this.Add(DayOfWeek.Tuesday);
			this.Add(DayOfWeek.Wednesday);
			this.Add(DayOfWeek.Thursday);
			this.Add(DayOfWeek.Friday);
		}

		#endregion

		#region public void AddWeekend()

		/// <summary>
		/// Adds Saturday and Sunday to the list.
		/// </summary>
		public void AddWeekend()
		{
			this.Add(DayOfWeek.Saturday);
			this.Add(DayOfWeek.Sunday);
		}

		#endregion

		#region public DayOfWeek GetNextDay(DayOfWeek after)

		/// <summary>
		/// Returns the next day from the list after the given day.
		/// </summary>
		/// <param name="after">The day after which to return the next day for.</param>
		public DayOfWeek GetNextDay(DayOfWeek after)
		{
			if (this.Count == 0)
			{
				throw new InvalidOperationException("This method cannot be invoked on an empty list.");
			}

			List<DayOfWeek> list = new List<DayOfWeek>();
			list.AddRange(this.weekdayArray);
			list.AddRange(this.weekdayArray);

			int i = list.IndexOf(after);
			i++;

			while (!this.Contains(list[i]))
			{
				i++;
			}

			return list[i];
		}

		#endregion

		#region public DayOfWeek GetPreviousDay(DayOfWeek before)

		/// <summary>
		/// Returns the previous day from the list before the given day.
		/// </summary>
		/// <param name="before">The day before which to return the previous day for.</param>
		public DayOfWeek GetPreviousDay(DayOfWeek before)
		{
			if (this.Count == 0)
			{
				throw new InvalidOperationException("This method cannot be invoked on an empty list.");
			}

			List<DayOfWeek> list = new List<DayOfWeek>();
			list.AddRange(this.weekdayArray);
			list.AddRange(this.weekdayArray);

			int i = list.LastIndexOf(before);
			i--;

			while (!this.Contains(list[i]))
			{
				i--;
			}

			return list[i];
		}

		#endregion

		#region public override string ToString()

		/// <summary>
		/// Returns the string representation of the weekday list.
		/// </summary>
		public override string ToString()
		{
			List<string> list = new List<string>();
			foreach (DayOfWeek day in this)
			{
				list.Add(day.ToString());
			}

			return string.Join(", ", list.ToArray());
		}

		#endregion

	}
}
